﻿using Microsoft.EntityFrameworkCore;
using NPOI.POIFS.Properties;
using StarsAdmin.Core.Enums;
using StarsAdmin.Core.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Linq.Expressions;
using System.Reflection;

namespace StarsAdmin.Core.Extensions
{
    public static class LinqExtension
    {
        public static async Task<List<TSource>> ToTreeAsync<TSource>(this IQueryable<TSource> source) where TSource : ITree<TSource>
        {
            // 如果源数据为空，直接返回空列表
            if (source == null || !source.Any())
            {
                return new List<TSource>();
            }
            // 从数据库中检索所有数据
            var allItems = await source.ToListAsync();

            // 将结果转换为字典，以便于后续查找
            var itemDictionary = allItems.ToDictionary(m => m.Id);
            // 找出所有顶级节点（没有父节点的节点）
            var parents = allItems.Where(e => !e.ParentId.HasValue || !itemDictionary.ContainsKey(e.ParentId.Value)).ToList();

            // 构建树结构
            var treeList = new List<TSource>();
            foreach (var parent in parents)
            {
                var treeNode = parent;
                treeNode.Children = new List<TSource>();
                // 递归查找子节点
                var children = FindChildren(allItems, treeNode.Id);
                treeNode.Children.AddRange(children);
                treeList.Add(treeNode);
            }
            return treeList;
        }

        private static List<TSource> FindChildren<TSource>(List<TSource> allItems, long parentId) where TSource : ITree<TSource>
        {
            var children = allItems
                .Where(e => e.ParentId == parentId)
                .Select(e =>
                {
                    var childNode = e;
                    childNode.Children = FindChildren(allItems, e.Id);
                    return childNode;
                })
                .ToList();

            return children;
        }

        /// <summary>
        /// 移除树形结构中重复的实体
        /// </summary>
        /// <typeparam name="TSource">实现 ITree 的类型</typeparam>
        /// <param name="source">原始实体列表</param>
        /// <returns>返回没有父级或者父级不在当前集合中的列表</returns>
        public static async Task<List<TSource>> ToHashSetTreeAsync<TSource>(this IQueryable<TSource> source) where TSource : ITree<TSource>
        {
            return await Task.FromResult(source.ToHashSetTree());
        }

        /// <summary>
        /// 移除树形结构中重复的实体
        /// </summary>
        /// <typeparam name="TSource">实现 ITree 的类型</typeparam>
        /// <param name="source">原始实体列表</param>
        /// <returns>返回没有父级或者父级不在当前集合中的列表</returns>
        public static List<TSource> ToHashSetTree<TSource>(this IQueryable<TSource> source) where TSource : ITree<TSource>
        {
            // 将结果转换为字典，以便于查找父项
            var itemDictionary = source.ToDictionary(m => m.Id);

            // 创建一个集合用于存储顶级实体（即没有父级或者父级不在当前集合中）
            var topItems = new List<TSource>();

            foreach (var item in source)
            {
                if (item.Children.Count > 0)
                {
                    topItems.Add(item);
                } else
                {
                    if (item.ParentId.HasValue && itemDictionary.ContainsKey(item.ParentId.Value))
                    {
                        var parent = itemDictionary[item.ParentId.Value];
                        var currentChildren = parent.Children.Where(e => e.Id == item.Id);
                        if (parent == null || currentChildren == null)
                        {
                            topItems.Add(item);
                        }
                    } else
                    {
                        topItems.Add(item);
                    }
                }
            }
            return topItems;
        }

        public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, IEnumerable<QueryCondition> conditions)
        {
            return condition ? source.Where(conditions) : source;
        }

        public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Expression<Func<TSource, bool>> predicate)
        {
            return condition ? source.Where(predicate) : source;
        }

        public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, bool condition, Func<TSource, bool> predicate)
        {
            return condition ? source.Where(predicate) : source;
        }

        public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, IEnumerable<QueryCondition> conditions)
        {
            if (conditions == null || !conditions.Any()) return source;

            Expression? expression = null;
            // 参数表达式
            ParameterExpression parameter = Expression.Parameter(typeof(TSource), "e");

            foreach (var condition in conditions)
            {
                // 成员表达式
                MemberExpression member = Expression.PropertyOrField(parameter, condition.Name);
                // 默认值的常量表达式，确保值类型与属性类型匹配
                ConstantExpression constant = ExpressionExtension.GetConstant(member.Type, condition.Value);
                // 第二个值的常量表达式，用于范围运算符的第二个值
                ConstantExpression? secondConstant = condition.SecondValue != null ? ExpressionExtension.GetConstant(member.Type, condition.SecondValue) : null;

                switch (condition.Operator)
                {
                    case Operators.Equal:
                        expression = expression.Append(Expression.Equal(member, constant));
                        break;

                    case Operators.NotEqual:
                        expression = expression.Append(Expression.NotEqual(member, constant));
                        break;

                    case Operators.GreaterThan:
                        expression = expression.Append(Expression.GreaterThan(member, constant));
                        break;

                    case Operators.LessThan:
                        expression = expression.Append(Expression.LessThan(member, constant));
                        break;

                    case Operators.GreaterThanOrEqual:
                        expression = expression.Append(Expression.GreaterThanOrEqual(member, constant));
                        break;

                    case Operators.LessThanOrEqual:
                        expression = expression.Append(Expression.LessThanOrEqual(member, constant));
                        break;

                    case Operators.Contains:

                        expression = expression.Append(ExpressionExtension.Contains(member, constant));
                        break;

                    case Operators.NotContains:

                        expression = expression.Append(ExpressionExtension.NotContains(member, constant));
                        break;

                    case Operators.StartWith:
                        expression = expression.Append(ExpressionExtension.StartsWith(member, constant));
                        break;

                    case Operators.EndWith:
                        expression = expression.Append(ExpressionExtension.EndsWith(member, constant));
                        break;

                    case Operators.Range:
                        expression = expression.Append(ExpressionExtension.Range(member, constant, secondConstant));
                        break;

                    case Operators.OutRange:
                        expression = expression.Append(ExpressionExtension.OutRange(member, constant, secondConstant));
                        break;

                    case Operators.None:
                        // 无操作，跳过此条件
                        break;

                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            var lambda = Expression.Lambda<Func<TSource, bool>>(expression, parameter);

            return source.Where(lambda);
        }

        public static IOrderedQueryable<TSource> OrderByIf<TSource>(this IQueryable<TSource> source, bool condition, string propertyName, bool order = true)
        {
            return (IOrderedQueryable<TSource>)(condition ? source.OrderBy(propertyName, order) : source);
        }

        public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string propertyName, bool order = true)
        {
            return order == false ? source.OrderByDescending(propertyName) : source.OrderBy(propertyName);
        }

        /// <summary>
        /// 顺序
        /// </summary>
        /// <typeparam name="TSousce"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static IOrderedQueryable<TSousce> OrderBy<TSousce>(this IQueryable<TSousce> source, string propertyName)
        {
            return Order(source, propertyName);
        }

        /// <summary>
        /// 倒序
        /// </summary>
        /// <typeparam name="TSousce"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static IOrderedQueryable<TSousce> OrderByDescending<TSousce>(this IQueryable<TSousce> source, string propertyName)
        {
            return Order(source, propertyName, false);
        }

        /// <summary>
        /// 顺序
        /// </summary>
        /// <typeparam name="TSousce"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static IOrderedQueryable<TSousce> ThenBy<TSousce>(this IQueryable<TSousce> source, string propertyName)
        {
            return Order(source, propertyName, true, true);
        }

        /// <summary>
        /// 倒序
        /// </summary>
        /// <typeparam name="TSousce"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static IOrderedQueryable<TSousce> ThenByDescending<TSousce>(this IQueryable<TSousce> source, string propertyName)
        {
            return Order(source, propertyName, false, true);
        }

        /// <summary>
        /// 排序，默认排序顺序为true，排序等级为false
        /// </summary>
        /// <typeparam name="TSousce"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName">属性名</param>
        /// <param name="order">默认true为顺序</param>
        /// <param name="level">默认false为OrderBy</param>
        /// <returns></returns>
        private static IOrderedQueryable<TSousce> Order<TSousce>(IQueryable<TSousce> source, string propertyName, bool order = true, bool level = false)
        {
            var param = Expression.Parameter(typeof(TSousce), "s");
            var property = Expression.PropertyOrField(param, propertyName);
            var lambda = Expression.Lambda(property, param);
            var arguments = Expression.Quote(lambda);

            var type = typeof(Queryable);

            var typeArguments = new[] { typeof(TSousce), property.Type };
            var method = (!level ? "OrderBy" : "ThenBy") + (order ? string.Empty : "Descending");
            var call = Expression.Call(type, method, typeArguments, source.Expression, arguments);

            return (IOrderedQueryable<TSousce>)source.Provider.CreateQuery<TSousce>(call);
        }

        /// <summary>
        /// 分页拓展
        /// </summary>
        /// <param name="source"></param>
        /// <param name="currentPage"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public static async Task<Page<TSource>> ToPageListAsync<TSource>(this IQueryable<TSource> source, int currentPage = 1, int pageSize = 20)
        {
            if (source == null)
            {
                return new Page<TSource>
                {
                    CurrentPage = currentPage,
                    PageSize = pageSize,
                    Items = null,
                    Total = 0,
                    PageCount = 0,
                };
            }
            int total = await source.CountAsync();
            if (total <= pageSize)
            {
                pageSize = total;
            }
            var items = await source.Skip(pageSize * (currentPage - 1)).Take(pageSize).ToListAsync();
            var pageCount = (int)Math.Ceiling(total / (double)pageSize);

            return new Page<TSource>
            {
                CurrentPage = currentPage,
                PageSize = pageSize,
                Items = items,
                Total = total,
                PageCount = pageCount,
            };
        }

        public static Page<T> ToPageList<T>(this IList<T> list, int currentPage, int pageSize)
        {
            if (list == null)
            {
                return new Page<T>
                {
                    CurrentPage = currentPage,
                    PageSize = pageSize,
                    Items = null,
                    Total = 0,
                    PageCount = 0,
                };
            }
            int total = list.Count;
            if (total <= pageSize)
            {
                pageSize = total;
            }
            var items = list?.Skip(pageSize * (currentPage - 1)).Take(pageSize);
            var pageCount = (int)Math.Ceiling(total / (double)pageSize);
            return new Page<T>
            {
                CurrentPage = currentPage,
                PageSize = pageSize,
                Items = items,
                Total = total,
                PageCount = pageCount,
            };
        }
    }
}